//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Workflow.ComponentModel.Design;
using System.ComponentModel.Design;
using Microsoft.Research.ScientificWorkflow.NetCDF.CSharpAPI;
using System.Workflow.ComponentModel.Compiler;

using Microsoft.Research.ScientificWorkflow;
using Microsoft.Research.ScientificWorkflow.Helpers;

using WorkflowData;

namespace Microsoft.Research.ScientificWorkflow.Activities
{
    [Name("Hyper Cube Sub Sample Transform")]
    [WorkflowCategory("NetCdf")]
    [Description("Subsamples data in a HyperCube structure")]
    public class HyperCubeSubsampler : Activity
    {
        #region Property Definitions
        
        public static DependencyProperty MinXProperty =
            DependencyProperty.Register("MinX", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MaxXProperty =
            DependencyProperty.Register("MaxX", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MinYProperty =
            DependencyProperty.Register("MinY", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MaxYProperty =
            DependencyProperty.Register("MaxY", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MinZProperty =
            DependencyProperty.Register("MinZ", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MaxZProperty =
            DependencyProperty.Register("MaxZ", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MinTProperty =
            DependencyProperty.Register("MinT", typeof(int), typeof(HyperCubeSubsampler));
        public static DependencyProperty MaxTProperty =
            DependencyProperty.Register("MaxT", typeof(int), typeof(HyperCubeSubsampler));

        [RequiredInputParam]
        [Name("Minimum X value", "Lower bound on the X dimnesion")]
        [Description("Lower bound on the X dimnesion.")]
        public int MinX
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MinXProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MinXProperty, value); }
        }

        [RequiredInputParam]
        [Name("Maximum X value", "Upper bound on the X dimnesion")]
        [Description("Upper bound on the X dimnesion.")]
        public int MaxX
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MaxXProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MaxXProperty, value); }
        }

        [RequiredInputParam]
        [Name("Minimum Y value", "Lower bound on the Y dimnesion")]
        [Description("Lower bound on the Y dimnesion.")]
        public int MinY
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MinYProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MinYProperty, value); }
        }

        [RequiredInputParam]
        [Name("Maximum Y value", "Upper bound on the Y dimnesion")]
        [Description("Upper bound on the Y dimnesion.")]
        public int MaxY
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MaxYProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MaxYProperty, value); }
        }

        [RequiredInputParam]
        [Name("Minimum Z value", "Lower bound on the Z dimnesion")]
        [Description("Lower bound on the Z dimnesion.")]
        public int MinZ
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MinZProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MinZProperty, value); }
        }

        [RequiredInputParam]
        [Name("Maximum Z value", "Upper bound on the Z dimnesion")]
        [Description("Upper bound on the Z dimnesion.")]
        public int MaxZ
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MaxZProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MaxZProperty, value); }
        }

        [RequiredInputParam]
        [Name("Minimum T value", "Lower bound on the T dimnesion")]
        [Description("Lower bound on the T dimnesion.")]
        public int MinT
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MinTProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MinTProperty, value); }
        }

        [RequiredInputParam]
        [Name("Maximum T value", "Upper bound on the T dimnesion")]
        [Description("Upper bound on the T dimnesion.")]
        public int MaxT
        {
            get { return ((int)(base.GetValue(HyperCubeSubsampler.MaxTProperty))); }
            set { base.SetValue(HyperCubeSubsampler.MaxTProperty, value); }
        }

        public static DependencyProperty CubeProperty =
            DependencyProperty.Register("Cube", typeof(HyperCube), typeof(HyperCubeSubsampler));

        [RequiredInputParam]
        [Name("First Hyper Cube")]
        [Description("First Hyper Cube for Merging")]
        public HyperCube Cube
        {
            get
            {
                return ((HyperCube)(base.GetValue(HyperCubeSubsampler.CubeProperty)));
            }
            set
            {
                base.SetValue(HyperCubeSubsampler.CubeProperty, value);
            }
        }

        public static DependencyProperty OutCubeProperty =
            DependencyProperty.Register("OutCube", typeof(HyperCube), typeof(HyperCubeSubsampler));

        [OutputParam]
        [Name("Resultant Hyper Cube")]
        [Description("Combined Hyper Cube containing merged data for specififed variable from both cubes.")]
        public HyperCube OutCube
        {
            get
            {
                return ((HyperCube)(base.GetValue(HyperCubeSubsampler.OutCubeProperty)));
            }
            set
            {
                base.SetValue(HyperCubeSubsampler.OutCubeProperty, value);
            }
        }

        #endregion

        public HyperCubeSubsampler()
        {

        }

        /// <summary>
        /// This method returns the max and min values for the four dimensions
        /// after checking in the variable varies accross that dimension.
        /// </summary>
        private void GetDimensionLengths(int colIndex, ref HyperCube inCube, ref int xSmallVal,
            ref int xLargeVal, ref int ySmallVal, ref int yLargeVal, ref int zSmallVal, ref int zLargeVal,
            ref int tSmallVal, ref int tLargeVal)
        {
            xSmallVal = 0; xLargeVal = 0; ySmallVal = 0; yLargeVal = 0; 
            zSmallVal = 0; zLargeVal = 0; tSmallVal = 0; tLargeVal = 0;
            // Check the dimensions 
            if (inCube.Schema.HasDimension(colIndex, HyperCube.Axis.X))
            {
                xSmallVal = MinX;
                xLargeVal = MaxX;
            }
            if (inCube.Schema.HasDimension(colIndex, HyperCube.Axis.Y))
            {
                ySmallVal = MinY;
                yLargeVal = MaxY;
            }
            if (inCube.Schema.HasDimension(colIndex, HyperCube.Axis.Z))
            {
                zSmallVal = MinZ;
                zLargeVal = MaxZ;
            }
            if (inCube.Schema.HasDimension(colIndex, HyperCube.Axis.T))
            {
                tSmallVal = MinT;
                tLargeVal = MaxT;
            }
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            HyperCube InputCube = this.Cube;

            HyperCubeSchema schema = this.Cube.Schema.Clone();
            
            int xLen = MaxX - MinX + 1;
            int yLen = MaxY - MinY + 1;
            int zLen = MaxZ - MinZ + 1;
            int tLen = MaxT - MinT + 1;

            HyperCube result = new HyperCube(schema, xLen, yLen, zLen, tLen);

            result.XDimensionValues = (null != InputCube.XDimensionValues) ?
                InputCube.XDimensionValues.GetRange(this.MinX, this.MaxX - this.MinX + 1) : null;
            result.YDimensionValues = (null != InputCube.YDimensionValues) ?
                InputCube.YDimensionValues.GetRange(this.MinY, this.MaxY - this.MinY + 1) : null;
            result.ZDimensionValues = (null != InputCube.ZDimensionValues) ?
                InputCube.ZDimensionValues.GetRange(this.MinZ, this.MaxZ - this.MinZ + 1) : null;
            result.TDimensionValues = (null != InputCube.TDimensionValues) ?
                InputCube.TDimensionValues.GetRange(this.MinT, this.MaxT - this.MinT + 1) : null;

            int xSmallVal = 0, xLargeVal = 0, ySmallVal = 0, yLargeVal = 0, zSmallVal = 0, 
                zLargeVal = 0, tSmallVal = 0, tLargeVal = 0;

            for (int i = 0; i < InputCube.Schema.ColumnCount; i++)
            {
                GetDimensionLengths(i, ref InputCube, ref xSmallVal, ref xLargeVal, ref ySmallVal,
                    ref yLargeVal, ref zSmallVal, ref zLargeVal, ref tSmallVal, ref tLargeVal);
                for (int minx = xSmallVal, x = 0; minx <= xLargeVal; minx++, x++)
                {
                    for (int miny = ySmallVal, y = 0; miny <= yLargeVal; miny++, y++)
                    {
                        for (int minz = zSmallVal, z = 0; minz <= zLargeVal; minz++, z++)
                        {
                            for (int mint = tSmallVal, t = 0; mint <= tLargeVal; mint++, t++)
                            {
                                result.SetData(InputCube[minx, miny, minz, mint][i], x, y, z, t, i);
                            }
                        }
                    }
                }
            }

            this.OutCube = result;
            return ActivityExecutionStatus.Closed;
        }

     }
}
